const fs = require('fs')
const path = require('path')
const CodeGen = require('./code_gen');

class ViewModelGen extends CodeGen {
  genHeader(json) {
    const desc = json.desc || "";
    const clsType = json.name;
    const clsName = this.toClassName(json.name);
    const objName = this.toObjName(json.name);
    const uclsName = clsName.toUpperCase();
    const vmClsName = this.toViewModelClassName(json.name);
    const vmClsType = this.toViewModelClassType(json.name);
    const header = path.basename(json.header);
    
    const elmClsType = json.annotation.collection;
    const elmClsName = this.toClassName(elmClsType);
    const vmElmName = this.toViewModelClassName(elmClsType);

    let result = `
/*This file is generated by code generator*/

#include "${header}"
#include "${vmElmName}.h"
#include "mvvm/base/view_model_array.h"

#ifndef TK_${uclsName}_VIEW_MODEL_H
#define TK_${uclsName}_VIEW_MODEL_H

BEGIN_C_DECLS
/**
 * @class ${vmClsType}
 *
 * view model of ${clsName}
 *
 */
typedef struct _${vmClsType} {
  view_model_array_t view_model_array;

  /*model object*/
  ${clsType}* ${objName};
  view_model_t* ${vmElmName};
} ${vmClsType};

/**
 * @method ${vmClsName}_create
 * 创建${clsName} view model对象。
 *
 * @annotation ["constructor"]
 * @param {navigator_request_t*} req 请求参数。
 *
 * @return {view_model_t} 返回view_model_t对象。
 */
view_model_t* ${vmClsName}_create(navigator_request_t* req);

/**
 * @method ${vmClsName}_create_with
 * 创建${clsName} view model对象。
 *
 * @annotation ["constructor"]
 * @param {${clsType}*}  ${objName} ${clsName}对象。
 *
 * @return {view_model_t} 返回view_model_t对象。
 */
view_model_t* ${vmClsName}_create_with(${clsType}* ${objName});

END_C_DECLS

#endif /*TK_${uclsName}_VIEW_MODEL_H*/
`

    return result;
  }

  genContent(json) {
    const desc = json.desc || "";
    const clsType = json.name;
    const objName = this.toObjName(json.name);
    const clsName = this.toClassName(json.name);
    const uclsName = clsName.toUpperCase();
    const setPropsDispatch = this.genSetPropDispatch(json);
    const getPropsDispatch = this.genGetPropDispatch(json);
    const canExecDispatch = this.genCanExecDispatch(json);
    const execDispatch = this.genExecDispatch(json);
    const constructor = this.genConstructor(json);
    const destructor = this.genDestructor(json);
    const forwardEvents = this.genForwardEvents(json);
    const offEvents = this.genOffEvents(json);
    const vmClsName = this.toViewModelClassName(json.name);
    const vmClsType = this.toViewModelClassType(json.name);

    const elmClsType = json.annotation.collection;
    const elmClsName = this.toClassName(elmClsType);
    const vmElmName = this.toViewModelClassName(elmClsType);

    let getItem = `${clsName}_get(${vmClsName}->${objName}, index)`
    if(this.isCpp(json)) {
      getItem = `${vmClsName}->${objName}->Get(index)`
    }
    let result = `
/*This file is generated by code generator*/

#include "tkc/mem.h"
#include "tkc/utils.h"
#include "mvvm/base/utils.h"
#include "${vmClsName}.h"

view_model_t* ${vmClsName}_attach(tk_object_t* obj, uint32_t index) {
  view_model_t* vm = VIEW_MODEL(obj);
  ${vmClsType}* ${vmClsName} = (${vmClsType}*)(vm);
  ${elmClsType}* ${elmClsName} = ${getItem};
  view_model_t* ${vmElmName} = ${vmClsName}->${vmElmName};

  ${vmElmName}_attach(${vmElmName}, ${elmClsName});

  return VIEW_MODEL(${vmElmName});
}

static ret_t ${vmClsName}_set_prop(tk_object_t* obj, const char* name, const value_t* v) {
  uint32_t index = 0;
  view_model_t* view_model = VIEW_MODEL(obj);
  ${clsType}* ${objName} = ((${vmClsType}*)(obj))->${objName};
  
  if(view_model_array_default_set_prop(view_model, name, v) == RET_OK) {
    return RET_OK;
  }

${setPropsDispatch}

  name = tk_destruct_array_prop_name(name, &index);
  view_model = ${vmClsName}_attach(obj, index);

  return view_model_set_prop(view_model, name, v);
}

static ret_t ${vmClsName}_get_prop(tk_object_t* obj, const char* name, value_t* v) {
  uint32_t index = 0;
  view_model_t* view_model = VIEW_MODEL(obj);
  ${clsType}* ${objName} = ((${vmClsType}*)(obj))->${objName};
  
  if(view_model_array_default_get_prop(view_model, name, v) == RET_OK) {
    return RET_OK;
  }

${getPropsDispatch}

  name = tk_destruct_array_prop_name(name, &index);
  view_model = ${vmClsName}_attach(obj, index);

  return view_model_get_prop(view_model, name, v);
}


static bool_t ${vmClsName}_can_exec(tk_object_t* obj, const char* name, const char* args) {
  uint32_t index = tk_atoi(args);
  view_model_t* view_model = VIEW_MODEL(obj);

${canExecDispatch}
  
  view_model = ${vmClsName}_attach(obj, index);

  return view_model_can_exec(view_model, name, NULL);
}

static ret_t ${vmClsName}_exec(tk_object_t* obj, const char* name, const char* args) {
  uint32_t index = tk_atoi(args);
  view_model_t* view_model = VIEW_MODEL(obj);

${execDispatch}

  view_model = ${vmClsName}_attach(obj, index);

  return view_model_exec(view_model, name, NULL);
}

static ret_t ${vmClsName}_on_destroy(tk_object_t* obj) {
  ${vmClsType}* vm = (${vmClsType}*)(obj);
  return_value_if_fail(vm != NULL, RET_BAD_PARAMS);

  ${offEvents}
  ${vmElmName}_attach(vm->${vmElmName}, NULL);
  TK_OBJECT_UNREF(vm->${vmElmName});
  ${destructor}(vm->${objName});

  return view_model_array_deinit(VIEW_MODEL(obj));
}

static const object_vtable_t s_${vmClsName}_vtable = {
  .type = "${vmClsType}",
  .desc = "${vmClsType}",
  .size = sizeof(${vmClsType}),
  .is_collection = TRUE,
  .on_destroy = ${vmClsName}_on_destroy,
  .compare = NULL,
  .get_prop = ${vmClsName}_get_prop,
  .set_prop = ${vmClsName}_set_prop,
  .remove_prop = NULL,
  .foreach_prop = NULL,
  .clear_props = NULL,
  .find_prop = NULL,
  .find_props = NULL,
  .can_exec = ${vmClsName}_can_exec,
  .exec = ${vmClsName}_exec,
  .clone = NULL
};

view_model_t* ${vmClsName}_create_with(${clsType}* ${objName}) {
  tk_object_t* obj = tk_object_create(&s_${vmClsName}_vtable);
  view_model_t* vm = view_model_array_init(VIEW_MODEL(obj));
  ${vmClsType}* ${vmClsName} = (${vmClsType}*)(vm);
  
  ${vmClsName}->${vmElmName} = ${vmElmName}_create_with(NULL);
  return_value_if_fail(vm != NULL, NULL);

  ${vmClsName}->${objName} = ${objName};
  ENSURE(${vmClsName}->${objName} != NULL);
  ${forwardEvents}

  return vm;
}

view_model_t* ${vmClsName}_create(navigator_request_t* req) {
  ${clsType}* ${objName} = ${constructor};
  return_value_if_fail(${objName} != NULL, NULL);

  return ${vmClsName}_create_with(${objName});
}
`;

    return result;
  }

  filter(json) {
    return json.filter(iter => {
      return this.isCollectionModel(iter)
    });
  }

  static run(filename) {
    const gen = new ViewModelGen();
    gen.genFile(filename);
  }
}

if (process.argv.length < 3) {
  console.log(`Usage: node index.js idl.json`);
  process.exit(0);
}

ViewModelGen.run(process.argv[2]);
